home *** CD-ROM | disk | FTP | other *** search
/ System Booster / System Booster.iso / Texteditors / GoldED Tools / Warp Parser / WarpLaTeX / source / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-27  |  45.3 KB  |  1,417 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  TeX scanner ©1995 Markus Aretz
  4.  
  5.  GoldED syntax parser (uses a syntax cache). Dice:
  6.  
  7.  DMAKE
  8.  
  9.  -------------------------------------------------------------------------------
  10.  
  11. */
  12.  
  13. #include "defs.h"
  14.  
  15. /// "Header stuff"
  16.  
  17. // Buffer handles are allocated for each text buffer to keep track of ressources:
  18.  
  19. struct BufferHandle {
  20.  
  21.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  22.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  23.     struct SyntaxInfo     *bh_SyntaxInfo;            // parser output
  24.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  25. };
  26.  
  27. // known TeX keywords are described by syntax structures:
  28.  
  29. struct Keyword {
  30.  
  31.     UBYTE *Pattern;                                  // known pattern (e.g. "UWORD")
  32.     UBYTE *Next;                                     // valid range for next non-space char  (or NULL)
  33.     UBYTE *Required;                                 // this char has to be on the same line (or NULL)
  34.     UWORD  Level;                                    // syntax level
  35. };
  36.  
  37. #define EMPTY_STACK ((struct SyntaxInfo *)~0)        // empty stack flag
  38.  
  39. // recognized syntax levels
  40.  
  41. #define SYNTAX_COMMENT          1
  42. #define SYNTAX_ENVIRONMENT      2
  43. #define SYNTAX_RESERVED         3
  44. #define SYNTAX_UNKNWNCMDS       4
  45. #define SYNTAX_NUMBER           5
  46. #define SYNTAX_TEXTFORMULA      6
  47. #define SYNTAX_FUNCTION         7
  48. #define SYNTAX_OPERATOR         8
  49. #define SYNTAX_BRACKET          9
  50. #define SYNTAX_ARROW            10
  51. #define SYNTAX_FONTATTR         11
  52. #define SYNTAX_DEFINE           12
  53. #define SYNTAX_DECLARE          13
  54.  
  55.  
  56. #define UPPER(a) ((a) & 95)                          // simple uppercase conversion
  57.  
  58. // syntax infos (or EMPTY_STACK) are attached to parsed lines
  59.  
  60. struct SyntaxInfo {
  61.  
  62.     UWORD              Flags;                        // comment flags
  63.     struct SyntaxChunk SyntaxChunk;                  // syntax chunks
  64. };
  65.  
  66. // these flag bits describe the line's comment properties
  67.  
  68. #define COMMENT_NONE      (0)                        // no comment in this line
  69. #define COMMENT_SIMPLE    (1L<<0)                    // there is a comment in this line
  70. #define COMMENT_START     (1L<<1)                    // line is start of multi-line comment
  71. #define COMMENT_BODY      (1L<<2)                    // line is body  of multi-line comment
  72. #define COMMENT_END       (1L<<3)                    // line is end   of multi-line comment
  73.  
  74. // macros to access SyntaxInfo members
  75.  
  76. #define GET_FLAGS(node)    (((struct SyntaxInfo *)(node)->UserData)->Flags)
  77. #define GET_CHUNK(node)   (&((struct SyntaxInfo *)(node)->UserData)->SyntaxChunk)
  78.  
  79. ///
  80. /// "Globals"
  81.  
  82. struct Keyword *Hash      [256];
  83. BOOL            IsOperator[256];
  84. BOOL            IsLetter  [256];
  85.  
  86. struct Keyword Reserved[] = {
  87.  
  88.     //  known_word next_char  on_same_line level
  89.  
  90.     // uppercase
  91.  
  92.  
  93.     // lowercase
  94.  
  95.     { "\\addtocounter",     NULL,       NULL,      SYNTAX_DECLARE },
  96.     { "\\addtolength",      NULL,       NULL,      SYNTAX_DEFINE  },
  97.     { "\\arccos",           NULL,       NULL,      SYNTAX_FUNCTION},
  98.     { "\\arcsin",           NULL,       NULL,      SYNTAX_FUNCTION},
  99.     { "\\arctan",           NULL,       NULL,      SYNTAX_FUNCTION},
  100.     { "\\arg",              NULL,       NULL,      SYNTAX_FUNCTION},
  101.     { "\\author",           "{",        NULL,      SYNTAX_DEFINE  },
  102.     { "\\begin",            "{",        NULL,      SYNTAX_RESERVED},
  103.     { "\\bf",               NULL,       NULL,      SYNTAX_FONTATTR},
  104.     { "\\bibitem",          "{[",       NULL,      SYNTAX_RESERVED},
  105.     { "\\bigskip",          NULL,       NULL,      SYNTAX_RESERVED},
  106.     { "\\bigskipamount",    NULL,       NULL,      SYNTAX_RESERVED},
  107.     { "\\cal",              NULL,       NULL,      SYNTAX_FONTATTR},
  108.     { "\\caption",          "{[",       NULL,      SYNTAX_RESERVED},
  109.     { "\\cdot",             NULL,       NULL,      SYNTAX_RESERVED},
  110.     { "\\cdots",            NULL,       NULL,      SYNTAX_RESERVED},
  111.     { "\\centerline",       "{",        NULL,      SYNTAX_RESERVED},
  112.     { "\\chapter",          "*{[",      "{",       SYNTAX_RESERVED},
  113.     { "\\cos",              NULL,       NULL,      SYNTAX_FUNCTION},
  114.     { "\\cosh",             NULL,       NULL,      SYNTAX_FUNCTION},
  115.     { "\\cot",              NULL,       NULL,      SYNTAX_FUNCTION},
  116.     { "\\coth",             NULL,       NULL,      SYNTAX_FUNCTION},
  117.     { "\\csc",              NULL,       NULL,      SYNTAX_FUNCTION},
  118.     { "\\date",             "{",        NULL,      SYNTAX_DEFINE  },
  119.     { "\\def",              NULL,       NULL,      SYNTAX_RESERVED},
  120.     { "\\deg",              NULL,       NULL,      SYNTAX_FUNCTION},
  121.     { "\\det",              NULL,       NULL,      SYNTAX_FUNCTION},
  122.     { "\\dim",              NULL,       NULL,      SYNTAX_FUNCTION},
  123.     { "\\documentclass",    "{[",       NULL,      SYNTAX_DECLARE },
  124.     { "\\documentstyle",    "{[",       NULL,      SYNTAX_DECLARE },
  125.     { "\\em",               NULL,       NULL,      SYNTAX_FONTATTR},
  126.     { "\\end",              "{",        NULL,      SYNTAX_RESERVED},
  127.     { "\\evensidemargin",   NULL,       NULL,      SYNTAX_RESERVED},
  128.     { "\\exp",              NULL,       NULL,      SYNTAX_FUNCTION},
  129.     { "\\footnote",         "{[",       "{",       SYNTAX_RESERVED},
  130.     { "\\footnotemark",     "{[",       "{",       SYNTAX_RESERVED},
  131.     { "\\footnotesize",     NULL,       NULL,      SYNTAX_FONTATTR},
  132.     { "\\frac",             "{",        "}",       SYNTAX_RESERVED},
  133.     { "\\gcd",              NULL,       NULL,      SYNTAX_FUNCTION},
  134.     { "\\hfill",            NULL,       NULL,      SYNTAX_RESERVED},
  135.     { "\\hline",            NULL,       NULL,      SYNTAX_RESERVED},
  136.     { "\\hom",              NULL,       NULL,      SYNTAX_FUNCTION},
  137.     { "\\huge",             NULL,       NULL,      SYNTAX_FONTATTR},
  138.     { "\\Huge",             NULL,       NULL,      SYNTAX_FONTATTR},
  139.     { "\\hyphenation",      "{",        NULL,      SYNTAX_RESERVED},
  140.     { "\\inf",              NULL,       NULL,      SYNTAX_FUNCTION},
  141.     { "\\include",          "{",        NULL,      SYNTAX_RESERVED},
  142.     { "\\includeonly",      "{",        NULL,      SYNTAX_RESERVED},
  143.     { "\\input",            NULL,       NULL,      SYNTAX_RESERVED},
  144.     { "\\it",               NULL,       NULL,      SYNTAX_FONTATTR},
  145.     { "\\item",             NULL,       NULL,      SYNTAX_RESERVED},
  146.     { "\\itemindent",       NULL,       NULL,      SYNTAX_DECLARE },
  147.     { "\\itemsep",          NULL,       NULL,      SYNTAX_DECLARE },
  148.     { "\\ker",              NULL,       NULL,      SYNTAX_FUNCTION},
  149.     { "\\label",            "{",        NULL,      SYNTAX_RESERVED},
  150.     { "\\LARGE",            NULL,       NULL,      SYNTAX_FONTATTR},
  151.     { "\\large",            NULL,       NULL,      SYNTAX_FONTATTR},
  152.     { "\\Large",            NULL,       NULL,      SYNTAX_FONTATTR},
  153.     { "\\ldots",            NULL,       NULL,      SYNTAX_RESERVED},
  154.     { "\\lg",               NULL,       NULL,      SYNTAX_FUNCTION},
  155.     { "\\lim",              NULL,       NULL,      SYNTAX_FUNCTION},
  156.     { "\\liminf",           NULL,       NULL,      SYNTAX_FUNCTION},
  157.     { "\\limsup",           NULL,       NULL,      SYNTAX_FUNCTION},
  158.     { "\\ln",               NULL,       NULL,      SYNTAX_FUNCTION},
  159.     { "\\log",              NULL,       NULL,      SYNTAX_FUNCTION},
  160.     { "\\makeglossary",     NULL,       NULL,      SYNTAX_RESERVED},
  161.     { "\\makeindex",        NULL,       NULL,      SYNTAX_RESERVED},
  162.     { "\\makelabel",        NULL,       NULL,      SYNTAX_RESERVED},
  163.     { "\\makelabels",       NULL,       NULL,      SYNTAX_RESERVED},
  164.     { "\\maketitle",        NULL,       NULL,      SYNTAX_RESERVED},
  165.     { "\\max",              NULL,       NULL,      SYNTAX_FUNCTION},
  166.     { "\\mbox",             "{",        NULL,      SYNTAX_RESERVED},
  167.     { "\\min",              NULL,       NULL,      SYNTAX_FUNCTION},
  168.     { "\\mit",              NULL,       NULL,      SYNTAX_FONTATTR},
  169.     { "\\newcommand",       "{",        "\\",      SYNTAX_DEFINE  },
  170.     { "\\newcounter",       "{",        NULL,      SYNTAX_DECLARE },
  171.     { "\\newpage",          NULL,       NULL,      SYNTAX_RESERVED},
  172.     { "\\normalsize",       NULL,       NULL,      SYNTAX_FONTATTR},
  173.     { "\\oddsidemargin",    NULL,       NULL,      SYNTAX_RESERVED},
  174.     { "\\pagebreak",        NULL,       NULL,      SYNTAX_RESERVED},
  175.     { "\\pagenumbering",    "{",        NULL,      SYNTAX_DECLARE },
  176.     { "\\pageref",          "{",        NULL,      SYNTAX_DECLARE },
  177.     { "\\pagestyle",        "{",        NULL,      SYNTAX_DECLARE },
  178.     { "\\par",              NULL,       NULL,      SYNTAX_RESERVED},
  179.     { "\\paragraph",        "*{[",      "{",       SYNTAX_RESERVED},
  180.     { "\\parbox",           "[{",       NULL,      SYNTAX_RESERVED},
  181.     { "\\parindent",        NULL,       NULL,      SYNTAX_DECLARE },
  182.     { "\\Pr",               NULL,       NULL,      SYNTAX_FUNCTION},
  183.     { "\\ref",              "{",        NULL,      SYNTAX_RESERVED},
  184.     { "\\renewcommand",     "{",        "\\",      SYNTAX_DEFINE  },
  185.     { "\\rm",               NULL,       NULL,      SYNTAX_FONTATTR},
  186.     { "\\sc",               NULL,       NULL,      SYNTAX_FONTATTR},
  187.     { "\\scriptsize",       NULL,       NULL,      SYNTAX_FONTATTR},
  188.     { "\\sec",              NULL,       NULL,      SYNTAX_FUNCTION},
  189.     { "\\section",          "[{",       "{",       SYNTAX_RESERVED},
  190.     { "\\setcounter",       NULL,       NULL,      SYNTAX_DEFINE  },
  191.     { "\\setlength",        NULL,       NULL,      SYNTAX_DEFINE  },
  192.     { "\\settowidth",       NULL,       NULL,      SYNTAX_DEFINE  },
  193.     { "\\sf",               NULL,       NULL,      SYNTAX_FONTATTR},
  194.     { "\\sin",              NULL,       NULL,      SYNTAX_FUNCTION},
  195.     { "\\sinh",             NULL,       NULL,      SYNTAX_FUNCTION},
  196.     { "\\sl",               NULL,       NULL,      SYNTAX_FONTATTR},
  197.     { "\\small",            NULL,       NULL,      SYNTAX_FONTATTR},
  198.     { "\\subsection",       "[{",       "{",       SYNTAX_RESERVED},
  199.     { "\\subsubsection",    "[{",       "{",       SYNTAX_RESERVED},
  200.     { "\\sup",              NULL,       NULL,      SYNTAX_FUNCTION},
  201.     { "\\tan",              NULL,       NULL,      SYNTAX_FUNCTION},
  202.     { "\\tanh",             NULL,       NULL,      SYNTAX_FUNCTION},
  203.     { "\\textheight",       NULL,       NULL,      SYNTAX_RESERVED},
  204.     { "\\textwidth",        NULL,       NULL,      SYNTAX_RESERVED},
  205.     { "\\tiny",             NULL,       NULL,      SYNTAX_FONTATTR},
  206.     { "\\title",            "{",        NULL,      SYNTAX_DEFINE  },
  207.     { "\\tt",               NULL,       NULL,      SYNTAX_FONTATTR},
  208.     { "\\typein",           "{[",       NULL,      SYNTAX_RESERVED},
  209.     { "\\typeout",          "{",        NULL,      SYNTAX_RESERVED},
  210.     { "\\unitlength",       NULL,       NULL,      SYNTAX_RESERVED},
  211.     { "\\usepackage",       "{[",       NULL,      SYNTAX_DECLARE},
  212.     { "\\vdots",            NULL,       NULL,      SYNTAX_RESERVED},
  213.     { "\\vline",            NULL,       NULL,      SYNTAX_RESERVED},
  214.     { "\\voffset",          NULL,       NULL,      SYNTAX_RESERVED},
  215.     { "\\vskip",            NULL,       NULL,      SYNTAX_RESERVED},
  216.     { "\\vspace",           "*{",       NULL,      SYNTAX_RESERVED},
  217.     { "abstract",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  218.     { "appendix",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  219.     { "array",              "}",        NULL,      SYNTAX_ENVIRONMENT},
  220.     { "center",             "}",        NULL,      SYNTAX_ENVIRONMENT},
  221.     { "description",        "}",        NULL,      SYNTAX_ENVIRONMENT},
  222.     { "displaymath",        "}",        NULL,      SYNTAX_ENVIRONMENT},
  223.     { "document",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  224.     { "empty",              "}",        NULL,      SYNTAX_ENVIRONMENT},
  225.     { "enumerate",          "}",        NULL,      SYNTAX_ENVIRONMENT},
  226.     { "eqnarray",           "*}",       "}",       SYNTAX_ENVIRONMENT},
  227.     { "equation",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  228.     { "figure",             "*}",       "}",       SYNTAX_ENVIRONMENT},
  229.     { "flushleft",          "}",        NULL,      SYNTAX_ENVIRONMENT},
  230.     { "flushright",         "}",        NULL,      SYNTAX_ENVIRONMENT},
  231.     { "fussypar",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  232.     { "headings",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  233.     { "itemize",            "}",        NULL,      SYNTAX_ENVIRONMENT},
  234.     { "letter",             "}",        NULL,      SYNTAX_ENVIRONMENT},
  235.     { "list",               "}",        NULL,      SYNTAX_ENVIRONMENT},
  236.     { "math",               "}",        NULL,      SYNTAX_ENVIRONMENT},
  237.     { "minipage",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  238.     { "myheadings",         "}",        NULL,      SYNTAX_ENVIRONMENT},
  239.     { "note",               "}",        NULL,      SYNTAX_ENVIRONMENT},
  240.     { "plain",              "}",        NULL,      SYNTAX_ENVIRONMENT},
  241.     { "picture",            "}",        NULL,      SYNTAX_ENVIRONMENT},
  242.     { "quotation",          "}",        NULL,      SYNTAX_ENVIRONMENT},
  243.     { "quote",              "}",        NULL,      SYNTAX_ENVIRONMENT},
  244.     { "samepage",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  245.     { "sloppypar",          "}",        NULL,      SYNTAX_ENVIRONMENT},
  246.     { "tabbing",            "}",        NULL,      SYNTAX_ENVIRONMENT},
  247.     { "table",              "*}",       "}",       SYNTAX_ENVIRONMENT},
  248.     { "tabular",            "*}",       "}",       SYNTAX_ENVIRONMENT},
  249.     { "thebibliography",    "}",        NULL,      SYNTAX_ENVIRONMENT},
  250.     { "theindex",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  251.     { "theorem",            "}",        NULL,      SYNTAX_ENVIRONMENT},
  252.     { "titlepage",          "}",        NULL,      SYNTAX_ENVIRONMENT},
  253.     { "trivlist",           "}",        NULL,      SYNTAX_ENVIRONMENT},
  254.     { "verbatim"            ,"}*",      "}",       SYNTAX_ENVIRONMENT},
  255.     {  NULL,                NULL,       NULL,      0}
  256. };
  257.  
  258. ///
  259. /// "Prototype"
  260.  
  261. // library functions
  262.  
  263. Prototype LibCall struct ParserData     *MountScanner(void);
  264. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  265. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  266. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  267. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  268. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  269. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  270. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  271. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  272.  
  273. // private functions
  274.  
  275. Prototype void                           PrepareHash(void);
  276. Prototype struct SyntaxChunk            *ParseString(UBYTE *, UWORD, ULONG);
  277. Prototype struct SyntaxInfo             *DupInfo(struct SyntaxInfo *);
  278. Prototype struct Keyword                *IsAReservedWord(UBYTE *, UWORD);
  279. Prototype struct Keyword                *IsAReservedOperator(UBYTE *, UWORD);
  280. Prototype BOOL                           strnchr(UBYTE *, UWORD, UWORD);
  281. Prototype BOOL                           CheckEnvironment(UBYTE *, UWORD, UWORD, struct Keyword *);
  282. Prototype struct LineNode               *FindContextStart(ULONG, struct LineNode *);
  283. Prototype void                           ParseContext(struct LineNode *, ULONG, ULONG);
  284. Prototype struct SyntaxInfo             *ParseContextLine(UBYTE *, UWORD, ULONG, ULONG);
  285. Prototype BOOL                           BadSuccessor(ULONG, ULONG);
  286. Prototype struct RefreshRequest         *VerifyContext(ULONG, ULONG, ULONG);
  287.  
  288. ///
  289. /// "Library functions"
  290.  
  291. /* ------------------------------- MountScanner --------------------------------
  292.  
  293.  Called by the editor before first usage of a scanner. Return a description of
  294.  our abilities.
  295.  
  296. */
  297.  
  298. LibCall struct ParserData *
  299. MountScanner()
  300. {
  301.     static struct ParserData parserData;
  302.  
  303.     // syntax elements understood by parser
  304.  
  305.     static UBYTE *levelNames[] = {
  306.  
  307.         "Standard Text",
  308.         "TeX - Kommentar",
  309.         "Umgebung",
  310.         "Reservierter Befehl",
  311.         "unbekannter Befehl",
  312.         "Zahl",
  313.         "Textformel",
  314.         "Funktion",
  315.         "Double operators",
  316.         "{}",
  317.         "Pfeil- und Zeigersymbole",
  318.         "Schriftarten",
  319.         "Definition",
  320.         "Erklärung",
  321.  
  322.         NULL
  323.     };
  324.  
  325.     // color suggestions
  326.  
  327.     static ULONG *levelColors[] = {
  328.  
  329.         MAKE_RGB4(0,  0,   0),                  // black
  330.         MAKE_RGB4(15, 15,  0),                  //
  331.         MAKE_RGB4(0,  15, 15),                  //
  332.         MAKE_RGB4(15,  0,  0),                  // red
  333.         MAKE_RGB4(0,  15,  0),                  // green
  334.         MAKE_RGB4(0,   0, 15),                  // blue
  335.         MAKE_RGB4(15, 15, 15),                  // white
  336.         MAKE_RGB4(15,  0, 15),                  //
  337.         MAKE_RGB4(0,   0,  0),                  // black
  338.         MAKE_RGB4(0,   0,  0),                  // black
  339.         MAKE_RGB4(15, 15, 15),                  // white
  340.         MAKE_RGB4(15, 15, 15),                  // white
  341.         MAKE_RGB4(15, 15, 15),                  // white
  342.         MAKE_RGB4(15, 15, 15),                  // white
  343.     };
  344.  
  345.     parserData.pd_Release  = SCANLIBVERSION;
  346.     parserData.pd_Version  = 1;
  347.     parserData.pd_Serial   = 0;
  348.     parserData.pd_Info     = "Warp LaTeX 0.3. ©'95 M.Aretz";
  349.     parserData.pd_Levels   = 14;
  350.     parserData.pd_Names    = levelNames;
  351.     parserData.pd_Colors   = levelColors;
  352.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
  353.     parserData.pd_Reserved = 0;
  354.  
  355.     PrepareHash();
  356.  
  357.     return(&parserData);
  358. }
  359.  
  360.  
  361. /* ------------------------------- StartScanner --------------------------------
  362.  
  363.  Called by the editor after a new text buffer has been created. We allocate a
  364.  buffer to hold text-specific data. The buffer address is returned as handle.
  365.  
  366. */
  367.  
  368. LibCall ULONG
  369. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
  370. {
  371.     struct BufferHandle *handle;
  372.  
  373.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  374.  
  375.         handle->bh_GlobalConfig = globalConfigPtr;
  376.         handle->bh_EditConfig   = editConfigPtr;
  377.         handle->bh_SyntaxInfo   = (struct SyntaxInfo *)syntaxStack;
  378.     }
  379.  
  380.     return((ULONG)handle);
  381. }
  382.  
  383.  
  384. /* ------------------------------- CloseScanner --------------------------------
  385.  
  386.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  387.  specific 'global' data.
  388.  
  389. */
  390.  
  391. LibCall ULONG
  392. CloseScanner(__D0 ULONG scanID)
  393. {
  394.     if (scanID) {
  395.  
  396.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  397.  
  398.         FreeVec(handle);
  399.     }
  400.  
  401.     return(0);
  402. }
  403.  
  404.  
  405. /* ------------------------------- FlushScanner --------------------------------
  406.  
  407.  Called by the editor in low memory situations: we are supposed to free as much
  408.  memory as possible.
  409.  
  410. */
  411.  
  412. LibCall void
  413. FlushScanner(__D0 ULONG scanID)
  414. {
  415.     struct BufferHandle *handle;
  416.     struct EditConfig   *config;
  417.     struct LineNode     *lineNode;
  418.  
  419.     handle = (struct BufferHandle *)scanID;
  420.     config = handle->bh_EditConfig;
  421.  
  422.     if (lineNode = config->TextNodes)
  423.         UnparseLines(lineNode, config->Lines);
  424. }
  425.  
  426.  
  427. /* ------------------------------- SetupScanner --------------------------------
  428.  
  429.  Called by the editor if the user wants to change the scanner's configuration.
  430.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  431.  unset).
  432.  
  433. */
  434.  
  435. LibCall void
  436. SetupScanner(__A0 globalConfigPtr)
  437. {
  438.     return();
  439. }
  440.  
  441.  
  442. /* ------------------------------- BriefScanner --------------------------------
  443.  
  444.  Called to notify a context scanner if lines have been added, deleted or
  445.  modified. We are supposed to return an additional display request or NULL
  446.  (the editor will refresh the damaged region only if we return NULL). Damaged
  447.  lines have already been unparsed by the editor. This is what we have to do:
  448.  
  449.  a) lines have been deleted or modified
  450.  
  451.     Check whether the syntax scheme of the remaining lines is still valid (it
  452.     will be invalid if say a comment start has been deleted but the comment
  453.     end has not been deleted). Reparse the lines and return a refresh request
  454.     if not.
  455.  
  456.  b) lines have been inserted
  457.  
  458.     Check whether the syntax sheme of the new lines affects the existing lines.
  459.     Reparse all lines starting at the insertion point and return a refresh
  460.     request if not.
  461.  
  462.  c) lines have been (un)folded
  463.  
  464.     This scanner assumes that folding doesn't affect syntax highlighting for the
  465.     sake of simplicity. This isn't necessarily a valid assumption: we will run
  466.     into touble if the user folds parts of a comment only (e.g. the first line).
  467.  
  468. */
  469.  
  470. LibCall struct RefreshRequest *
  471. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  472. {
  473.     struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  474.  
  475.     switch (notify->sn_Class) {
  476.  
  477.         case SCANNER_NOTIFY_MODIFIED:
  478.  
  479.             ULONG line = notify->sn_Line;
  480.  
  481.             if (notify->sn_Removed > notify->sn_Lines) {
  482.  
  483.                 // lines have been deleted
  484.  
  485.                 return(VerifyContext(scanID, line, 0));
  486.             }
  487.             else if (notify->sn_Lines)
  488.  
  489.                 // lines have been modified or inserted
  490.  
  491.                 return(VerifyContext(scanID, line, notify->sn_Lines));
  492.             else
  493.                 return(NULL);
  494.  
  495.             break;
  496.  
  497.         default:
  498.  
  499.             return(NULL);
  500.     }
  501. }
  502.  
  503.  
  504. /* --------------------------------- ParseLine ---------------------------------
  505.  
  506.  Parse a line, build a syntax description
  507.  
  508. */
  509.  
  510. LibCall struct SyntaxChunk *
  511. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  512. {
  513.     if (lineNode->Fold)
  514.  
  515.         return(NULL);
  516.  
  517.     else if (lineNode->Len) {
  518.  
  519.         // not yet preparsed ? preparse a couple of lines
  520.  
  521.         if (lineNode->UserData == EMPTY_STACK)
  522.  
  523.             return(NULL);
  524.  
  525.         else if (lineNode->UserData)
  526.  
  527.             return(GET_CHUNK(lineNode));
  528.  
  529.         else {
  530.  
  531.             ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line + 1;
  532.  
  533.             if (lines > 50)
  534.                 lines = 50;
  535.  
  536.             ParseSection(scanID, lineNode, lines);
  537.  
  538.             if (lineNode->UserData == EMPTY_STACK)
  539.                 return(NULL);
  540.             else
  541.                 return(GET_CHUNK(lineNode));
  542.         }
  543.     }
  544.     else
  545.         return(NULL);
  546. }
  547.  
  548.  
  549. /* -------------------------------- UnparseLines -------------------------------
  550.  
  551.  Called by the editor if lines are to be deleted. We are supposed to free
  552.  private data attached to the lines.
  553.  
  554. */
  555.  
  556. LibCall void
  557. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  558. {
  559.     while (lines--) {
  560.  
  561.         // free syntax cache
  562.  
  563.         if (lineNode->UserData) {
  564.  
  565.             if (lineNode->UserData != (APTR)EMPTY_STACK)
  566.                 FreeVec(lineNode->UserData);
  567.  
  568.             lineNode->UserData = NULL;
  569.         }
  570.  
  571.         // free folded subblock
  572.  
  573.         if (lineNode->Fold)
  574.             UnparseLines(lineNode->Fold->TextNodes, lineNode->Fold->Lines);
  575.  
  576.         ++lineNode;
  577.     }
  578. }
  579.  
  580. /* -------------------------------- ParseSection -------------------------------
  581.  
  582.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  583.  preparse the lines.
  584.  
  585. */
  586.  
  587. LibCall void
  588. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  589. {
  590.     struct LineNode *firstNode;
  591.  
  592.     if (firstNode = FindContextStart(scanID, lineNode))
  593.         ParseContext(firstNode, lines + (lineNode - firstNode), scanID);
  594. }
  595.  
  596. ///
  597. /// "private"
  598.  
  599. /* -------------------------------- PrepareHash --------------------------------
  600.  
  601.  Prepare reserved keyword hashtable (used to find keyword description if ascii
  602.  value of first letter is known) and prepare IsOperator array.
  603.  
  604. */
  605.  
  606. void
  607. PrepareHash()
  608. {
  609.     struct Keyword *keyword;
  610.     UWORD           ascii;
  611.  
  612.     memset(Hash, 0, sizeof(Hash));
  613.  
  614.     keyword = Reserved;
  615.  
  616.     while (keyword->Pattern) {
  617.  
  618.         UWORD ascii = *keyword->Pattern;
  619.  
  620.         Hash[ascii] = keyword;
  621.  
  622.         while (keyword->Pattern && (*keyword->Pattern == ascii))
  623.             ++keyword;
  624.     }
  625.  
  626.     memset(IsOperator, FALSE, sizeof(IsOperator));
  627.  
  628.     IsOperator['#'] = TRUE;
  629.     IsOperator['&'] = TRUE;
  630.     IsOperator['_'] = TRUE;
  631.     IsOperator['{'] = TRUE;
  632.     IsOperator['}'] = TRUE;
  633.     IsOperator['-'] = TRUE;
  634.     IsOperator['+'] = TRUE;
  635.     IsOperator['/'] = TRUE;
  636.     IsOperator['*'] = TRUE;
  637.  
  638.     memset(IsLetter, FALSE, sizeof(IsLetter));
  639.  
  640.     for (ascii = 0; ascii <= 255; ++ascii)
  641.         IsLetter[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z')) || ascii == '\\';
  642. }
  643.  
  644.  
  645.  
  646.  
  647. /* ------------------------------ IsAReservedWord ------------------------------
  648.  
  649.  Decide whether word of length <len> is a reserved LaTeX keyword
  650.  
  651. */
  652.  
  653. struct Keyword *
  654. IsAReservedWord(text, len)
  655.  
  656. UBYTE *text;
  657. UWORD  len;
  658. {
  659.     struct Keyword *keyword;
  660.  
  661.     if (keyword = Hash[*text]) {
  662.  
  663.         while (keyword->Pattern) {
  664.  
  665.             if (*keyword->Pattern > *text)
  666.  
  667.                 break;
  668.  
  669.             else if (keyword->Pattern[len] || (keyword->Pattern[1] != text[1]) || memcmp(text, keyword->Pattern, len))
  670.  
  671.                 ++keyword;
  672.             else
  673.                 return(keyword);
  674.         }
  675.  
  676.         return(NULL);
  677.     }
  678.     else
  679.         return(NULL);
  680. }
  681.  
  682.  
  683. /* ------------------------------ IsAReservedOperator --------------------------
  684.  
  685.  Decide whether word of length <len> is a reserved C 'double' operator
  686.  
  687. */
  688.  
  689. struct Keyword *
  690. IsAReservedOperator(text, len)
  691.  
  692. UBYTE *text;
  693. UWORD  len;
  694. {
  695.     static struct Keyword reserved[] = {
  696.  
  697.     //  known word    next char   on same line
  698.  
  699.         { "\#",         NULL,        NULL,      8} ,
  700.         { "\$",         NULL,        NULL,      8} ,
  701.         { "\&",         NULL,        NULL,      8} ,
  702.         { "\_",         NULL,        NULL,      8} ,
  703.         { "\{",         NULL,        NULL,      8} ,
  704.         { "\}",         NULL,        NULL,      8} ,
  705.         { "--",         NULL,        NULL,      8} ,
  706.         { "---",         NULL,        NULL,      8} ,
  707.         { "->",         NULL,        NULL,     10} ,
  708.         { "/=",         NULL,        NULL,      8} ,
  709.         { "::",         NULL,        NULL,      8} ,
  710.         { "<<",         NULL,        NULL,      8} ,
  711.         { "==",         NULL,        NULL,      8} ,
  712.         { ">=",         NULL,        NULL,      8} ,
  713.         { ">>",         NULL,        NULL,      8} ,
  714.         { "|=",         NULL,        NULL,      8} ,
  715.         {  NULL,        NULL,        NULL,      0}
  716.     };
  717.  
  718.     struct Keyword *keyword;
  719.  
  720.     for (keyword = reserved; keyword->Pattern; ++keyword) {
  721.  
  722.         if (*keyword->Pattern > *text)
  723.  
  724.             break;
  725.  
  726.         else if ((*keyword->Pattern == *text) && (keyword->Pattern[1] == text[1]))
  727.  
  728.             return(keyword);
  729.     }
  730.  
  731.     return(NULL);
  732. }
  733.  
  734.  
  735.  
  736. /* ----------------------------- CheckEnvironment ------------------------------
  737.  
  738.  Check whether keyword environment complies with keyword.
  739.  
  740. */
  741.  
  742. BOOL
  743. CheckEnvironment(text, len, wordLen, keyword)
  744.  
  745. struct Keyword *keyword;
  746. UBYTE          *text;
  747. UWORD           len, wordLen;
  748. {
  749.     // move to first non-space character after recognized keyword
  750.  
  751.     for (text += wordLen, len -= wordLen; len && (*text == 32); ++wordLen, --len)
  752.         ++text;
  753.  
  754.     // check whether first non-space character is valid
  755.  
  756.     if ((keyword->Next == NULL) || strchr(keyword->Next, *text)) {
  757.  
  758.         // check whether required character is used on the same line
  759.  
  760.         if ((keyword->Required == NULL) || strnchr(text, len, *keyword->Required)) {
  761.  
  762.             return(TRUE);
  763.         }
  764.         else
  765.             return(FALSE);
  766.     }
  767.     else
  768.         return(FALSE);
  769. }
  770.  
  771.  
  772. /* ---------------------------------- strnchr ----------------------------------
  773.  
  774.  strchr replacement (doesn't require 0-terminated string). Return TRUE if the
  775.  character is found within <text>.
  776.  
  777. */
  778.  
  779. BOOL
  780. strnchr(text, len, character)
  781.  
  782. UBYTE *text;
  783. UWORD  character, len;
  784. {
  785.     while (len--)
  786.         if (*text++ == character)
  787.             return(TRUE);
  788.  
  789.     return(FALSE);
  790. }
  791.  
  792.  
  793. /* --------------------------------- DupInfo -----------------------------------
  794.  
  795.  Duplicate syntax info (to be FreeVec'ed)
  796.  
  797. */
  798.  
  799. struct SyntaxInfo *
  800. DupInfo(syntaxInfo)
  801.  
  802. struct SyntaxInfo *syntaxInfo;
  803. {
  804.     struct SyntaxChunk *chunk;
  805.     struct SyntaxInfo  *info;
  806.     ULONG               size;
  807.     UWORD               elements;
  808.  
  809.     // determine stack size
  810.  
  811.     for (elements = 0, chunk = &syntaxInfo->SyntaxChunk; chunk->sc_Level; ++chunk)
  812.         ++elements;
  813.  
  814.     // create copy of syntax stack (to be attached to a text line by the caller)
  815.  
  816.     size = (++elements) * sizeof(struct SyntaxChunk) + sizeof(UWORD);
  817.  
  818.     if (info = AllocVec(size + sizeof(UWORD), MEMF_PUBLIC)) {
  819.  
  820.         movmem(syntaxInfo, info, size);
  821.  
  822.         return(info);
  823.     }
  824.     else
  825.         return(NULL);
  826. }
  827.  
  828.  
  829. /*  ----------------------------- FindContextStart ------------------------------
  830.  
  831.  Search backwards until a parsed line or a context free line (fold or start of
  832.  file) is found. Return line node. This node may be used as starting point for
  833.  parsing lines.
  834.  
  835. */
  836.  
  837. struct LineNode *
  838. FindContextStart(scanID, lineNode)
  839.  
  840. ULONG            scanID;
  841. struct LineNode *lineNode;
  842. {
  843.     ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
  844.  
  845.     while (line--) {
  846.  
  847.         // found a fold or a parsed line ?
  848.  
  849.         if (lineNode->Fold || lineNode->UserData)
  850.             break;
  851.  
  852.         --lineNode;
  853.     }
  854.  
  855.     return(lineNode);
  856. }
  857.  
  858.  
  859. /* ------------------------------- ParseContext --------------------------------
  860.  
  861.  Preparse lines. The first line is expected to be either a context free line
  862.  (start of file or a fold header) or to be a parsed line.
  863.  
  864. */
  865.  
  866. void
  867. ParseContext(lineNode, lines, scanID)
  868.  
  869. struct LineNode *lineNode;
  870. ULONG            lines, scanID;
  871. {
  872.     ULONG mode;
  873.  
  874.     for (mode = COMMENT_NONE; lines--; ++lineNode) {
  875.  
  876.         if (lineNode->Fold)
  877.  
  878.             mode = COMMENT_NONE;
  879.  
  880.         else {
  881.  
  882.             if (lineNode->UserData == EMPTY_STACK)
  883.  
  884.                 mode = COMMENT_NONE;
  885.  
  886.             else if (lineNode->UserData)
  887.  
  888.                 mode = GET_FLAGS(lineNode);
  889.  
  890.             else {
  891.  
  892.                 struct SyntaxInfo *syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, mode, scanID);
  893.  
  894.                 if (syntaxInfo == EMPTY_STACK) {
  895.  
  896.                     lineNode->UserData = EMPTY_STACK;
  897.  
  898.                     mode = COMMENT_NONE;
  899.                 }
  900.                 else if (syntaxInfo) {
  901.  
  902.                     lineNode->UserData = DupInfo(syntaxInfo);
  903.  
  904.                     mode = syntaxInfo->Flags;
  905.                 }
  906.                 else
  907.                     mode = COMMENT_NONE;
  908.             }
  909.         }
  910.     }
  911. }
  912.  
  913.  
  914. /* ----------------------------- ParseContextLine ------------------------------
  915.  
  916.  Parse a string, build a syntax description. Return EMPTY_STACK in case there is
  917.  nothing to highlight. Check whether there are comments. The status of the last
  918.  line is passed in by the caller, this function will return the status (flag
  919.  bits) of the new line.
  920.  
  921. */
  922.  
  923. struct SyntaxInfo *
  924. ParseContextLine(text, len, last, scanID)
  925.  
  926. UBYTE *text;
  927. UWORD  len;
  928. ULONG  last, scanID;
  929. {
  930.     if (len) {
  931.  
  932.         struct SyntaxInfo  *syntaxInfo;
  933.         struct SyntaxChunk *syntaxStack;
  934.         struct Keyword     *keyword;
  935.  
  936.         BOOL   inString, inComment, anyText, innerComment;
  937.         UBYTE *next;
  938.         UWORD  indent, startString, startComment, element, lastChar, wordLen;
  939.         ULONG  status;
  940.  
  941.         syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  942.  
  943.         // leading spaces have to be ignored
  944.  
  945.         for (indent = 0; len && (*text == 32); ++indent, --len)
  946.             ++text;
  947.  
  948.         // trailing spaces have to be ignored
  949.  
  950.          while (len && ((text[len - 1] == 32)))
  951.             --len;
  952.  
  953.         // preset comment status based on status of previous line
  954.  
  955.         if (last & (COMMENT_START | COMMENT_BODY)) {
  956.  
  957.             status    = COMMENT_BODY;
  958.             inComment = TRUE;
  959.         }
  960.         else {
  961.  
  962.             status    = COMMENT_NONE;
  963.             inComment = FALSE;
  964.         }
  965.  
  966.         startComment = 0;
  967.         innerComment = FALSE;
  968.  
  969.         inString = FALSE;
  970.         anyText  = FALSE;
  971.         lastChar = 32;
  972.  
  973.         syntaxStack = &syntaxInfo->SyntaxChunk;
  974.  
  975.         for (element = 0; len; ++text, ++indent, --len) {
  976.  
  977.             if (*text != 32) {
  978.  
  979.                 if (inComment) {
  980.  
  981.                     // end of comment detected ?
  982.  
  983.                     if ((*text == '*') && (text[1] == '/') && (len > 1)) {
  984.  
  985.                         // end of a multi-line comment or end of inner line comment ?
  986.  
  987.                         if (innerComment)
  988.  
  989.                             status |= COMMENT_SIMPLE;
  990.  
  991.                         else {
  992.  
  993.                             status |=  COMMENT_END;
  994.                             status &= ~COMMENT_BODY;
  995.                         }
  996.  
  997.                         syntaxStack[element].sc_Level = SYNTAX_COMMENT;
  998.                         syntaxStack[element].sc_Start = startComment;
  999.                         syntaxStack[element].sc_End   = indent + 1;
  1000.  
  1001.                         ++element;
  1002.  
  1003.                         inComment = FALSE;
  1004.  
  1005.                         ++text;
  1006.                         ++indent;
  1007.                         --len;
  1008.                     }
  1009.                 }
  1010.                 else if ((*text == 36) && ((inString == FALSE) || (*(text - 1) != 92))) {
  1011.  
  1012.                     if (inString = !inString)
  1013.  
  1014.                         startString = indent;
  1015.  
  1016.                     else if (indent > startString) {
  1017.  
  1018.                         // end of string detected
  1019.  
  1020.                         syntaxStack[element].sc_Level = SYNTAX_TEXTFORMULA;
  1021.                         syntaxStack[element].sc_Start = startString;
  1022.                         syntaxStack[element].sc_End   = indent;
  1023.  
  1024.                         ++element;
  1025.                     }
  1026.                 }
  1027.                 else if (inString == FALSE) {
  1028.  
  1029.                     if (IsLetter[*text]) {
  1030.  
  1031.                         wordLen = 1;
  1032.  
  1033.                         while (IsLetter[text[wordLen]] && (wordLen < len) && (text[wordLen] != '\\'))
  1034.                             ++wordLen;
  1035.  
  1036.                         // reserved word detected ?
  1037.  
  1038.                         if (Hash[*text]) {
  1039.  
  1040.                             if ((lastChar != '\\')) {
  1041.  
  1042.                                 if (keyword = IsAReservedWord(text, wordLen)) {
  1043.  
  1044.                                     // environment to be checked ?
  1045.  
  1046.                                     if (keyword->Next || keyword->Required) {
  1047.  
  1048.                                         if (CheckEnvironment(text, len, wordLen, keyword)) {
  1049.  
  1050.                                             syntaxStack[element].sc_Level = keyword->Level;
  1051.                                             syntaxStack[element].sc_Start = indent;
  1052.                                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1053.  
  1054.                                             ++element;
  1055.                                         }
  1056.                                     }
  1057.                                     else {
  1058.  
  1059.                                         syntaxStack[element].sc_Level = keyword->Level;
  1060.                                         syntaxStack[element].sc_Start = indent;
  1061.                                         syntaxStack[element].sc_End   = indent + wordLen - 1;
  1062.  
  1063.                                         ++element;
  1064.                                     }
  1065.                                 }
  1066.                             }
  1067.                         }
  1068.  
  1069.                         // move to next section (consider end-of-loop action)
  1070.  
  1071.                         --wordLen;
  1072.  
  1073.                         text   += wordLen;
  1074.                         indent += wordLen;
  1075.                         len    -= wordLen;
  1076.                     }
  1077.                     else if ((*text >= '0') && (*text <= '9')) {
  1078.  
  1079.                         // number detected
  1080.  
  1081.                         BOOL isHex = (*text == '0') && (text[1] == 'x');
  1082.  
  1083.                         for (next = text + (wordLen = 1); (((*next <= '9') && (*next >= '0')) || (isHex && ((*next == 'x') || ((UPPER(*next) >= 'A') && (UPPER(*next) <= 'F'))))) && (wordLen < len); ++next)
  1084.                             ++wordLen;
  1085.  
  1086.                         // ignore numbers attached to letters (e.g. __A0)
  1087.  
  1088.                         if (IsLetter[lastChar] == FALSE) {
  1089.  
  1090.                             syntaxStack[element].sc_Level = SYNTAX_NUMBER;
  1091.                             syntaxStack[element].sc_Start = indent;
  1092.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1093.  
  1094.                             ++element;
  1095.                         }
  1096.  
  1097.                         // move to next section (consider end-of-loop action)
  1098.  
  1099.                         --wordLen;
  1100.  
  1101.                         text   += wordLen;
  1102.                         indent += wordLen;
  1103.                         len    -= wordLen;
  1104.                     }
  1105.                     else if ((*text == '%') && (*(text - 1) != '\\')) {
  1106.  
  1107.                         // TeX comment detected (indicated by %)
  1108.  
  1109.                         syntaxStack[element].sc_Level = SYNTAX_COMMENT;
  1110.                         syntaxStack[element].sc_Start = indent;
  1111.                         syntaxStack[element].sc_End   = indent + len - 1;
  1112.  
  1113.                         ++element;
  1114.  
  1115.                         // terminate parsing (rest of line is a comment)
  1116.  
  1117.                         break;
  1118.                     }
  1119.                     else if ((*text == '/') && (text[1] == '*') && (len > 1)) {
  1120.  
  1121.                         // start of comment detected
  1122.  
  1123.                         inComment    = TRUE;
  1124.                         innerComment = TRUE;
  1125.                         startComment = indent;
  1126.  
  1127.                         ++text;
  1128.                         ++indent;
  1129.                         --len;
  1130.                     }
  1131.                     else if (IsOperator[*text]) {
  1132.  
  1133.                         wordLen = 1;
  1134.  
  1135.                         while (IsOperator[text[wordLen]] && (wordLen < len))
  1136.                             ++wordLen;
  1137.  
  1138.                         // reserved operator detected ?
  1139.  
  1140.                         if (keyword = IsAReservedOperator(text, wordLen)) {
  1141.  
  1142.                             syntaxStack[element].sc_Level = keyword->Level;
  1143.                             syntaxStack[element].sc_Start = indent;
  1144.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1145.  
  1146.                             ++element;
  1147.                         }
  1148.  
  1149.                         // move to next section (consider end-of-loop action)
  1150.  
  1151.                         --wordLen;
  1152.  
  1153.                         text   += wordLen;
  1154.                         indent += wordLen;
  1155.                         len    -= wordLen;
  1156.                     }
  1157.                     else if ((*text == '{') || (*text == '}')) {
  1158.  
  1159.                         // bracket detected
  1160.  
  1161.                         syntaxStack[element].sc_Level = SYNTAX_BRACKET;
  1162.                         syntaxStack[element].sc_Start = indent;
  1163.                         syntaxStack[element].sc_End   = indent;
  1164.  
  1165.                         ++element;
  1166.                     }
  1167.                     else if (*text == '\\') {
  1168.  
  1169.                         // only spaces detected so far ?
  1170.  
  1171.                         if (anyText == FALSE) {
  1172.  
  1173.                             // any LaTeX command
  1174.  
  1175.                             wordLen = 1;
  1176.  
  1177.                             for (wordLen = 1; (wordLen < len) && (text[wordLen] != 32) && (text[wordLen] != '\\') && (text[wordLen] != '{') && (text[wordLen] != '['); ++wordLen)
  1178.                                 if ((text[wordLen] == '/') && (wordLen + 1 < len) && (text[wordLen + 1] == '/'))
  1179.                                     break;
  1180.  
  1181.                             syntaxStack[element].sc_Level = SYNTAX_UNKNWNCMDS;
  1182.                             syntaxStack[element].sc_Start = indent;
  1183.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1184.  
  1185.                             // move to end of reserved word (consider end-of-loop action)
  1186.  
  1187.                             --wordLen;
  1188.  
  1189.                             text   += wordLen;
  1190.                             indent += wordLen;
  1191.                             len    -= wordLen;
  1192.  
  1193.                             ++element;
  1194.                         }
  1195.                     }
  1196.                 }
  1197.  
  1198.                 anyText = TRUE;
  1199.             }
  1200.  
  1201.             lastChar = *text;
  1202.         }
  1203.  
  1204.         // comment not closed ?
  1205.  
  1206.         if (inComment) {
  1207.  
  1208.             // continuation of multi-line comment or new (inner line) comment ?
  1209.  
  1210.             if (innerComment)
  1211.                 status |= COMMENT_START;
  1212.             else
  1213.                 status |= COMMENT_BODY;
  1214.  
  1215.             syntaxStack[element].sc_Level = SYNTAX_COMMENT;
  1216.             syntaxStack[element].sc_Start = startComment;
  1217.             syntaxStack[element].sc_End   = indent - 1;
  1218.  
  1219.             ++element;
  1220.         }
  1221.  
  1222.         if (element) {
  1223.  
  1224.             // terminate syntax stack
  1225.  
  1226.             syntaxStack[element].sc_Start = FALSE;
  1227.             syntaxStack[element].sc_End   = FALSE;
  1228.             syntaxStack[element].sc_Level = FALSE;
  1229.  
  1230.             syntaxInfo->Flags = status;
  1231.  
  1232.             return(syntaxInfo);
  1233.         }
  1234.         else
  1235.             return(EMPTY_STACK);
  1236.     }
  1237.     else if (last) {
  1238.  
  1239.         if (last & (COMMENT_START | COMMENT_BODY)) {
  1240.  
  1241.             struct SyntaxInfo *syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  1242.  
  1243.             syntaxInfo->SyntaxChunk.sc_Start = FALSE;
  1244.             syntaxInfo->SyntaxChunk.sc_End   = FALSE;
  1245.             syntaxInfo->SyntaxChunk.sc_Level = FALSE;
  1246.  
  1247.             syntaxInfo->Flags = COMMENT_BODY;
  1248.  
  1249.             return(syntaxInfo);
  1250.         }
  1251.         else
  1252.             return(EMPTY_STACK);
  1253.     }
  1254.     else
  1255.         return(EMPTY_STACK);
  1256. }
  1257.  
  1258.  
  1259. /* ------------------------------- VerifyContext -------------------------------
  1260.  
  1261.  Ensure that syntax information of a specified range of lines ("damage region")
  1262.  is valid and consistent with the existing text. Return a refresh request if
  1263.  not. The damage area is expected to have been unparsed already. This is what we
  1264.  have to do: we preparse existing lines before the damage area if belonging to
  1265.  the syntax context of the damage area (ie. all lines affecting highlighting of
  1266.  the first line in the damage area). The damage area is parsed, too. Parsed
  1267.  lines after the damage area are reparsed if highlighting is found to be
  1268.  inconsistent with the line(s) before. Reparsing continues until the end of the
  1269.  file respectively until no more inconsistencies are found.
  1270.  
  1271. */
  1272.  
  1273. struct RefreshRequest *
  1274. VerifyContext(scanID, line, lines)
  1275.  
  1276. ULONG scanID, line, lines;
  1277. {
  1278.     struct EditConfig *config;
  1279.     struct LineNode   *lineNode, *lastNode;
  1280.     struct SyntaxInfo *syntaxInfo;
  1281.  
  1282.     ULONG last, new, refreshStart, refresh = FALSE;
  1283.  
  1284.     config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  1285.  
  1286.     lineNode = config->TextNodes + line;
  1287.     lastNode = config->TextNodes + line + lines - 1;
  1288.  
  1289.     // preparse from context start until end of damage area
  1290.  
  1291.     ParseSection(scanID, lineNode, lines);
  1292.  
  1293.     // get syntax flags of last line in damage area
  1294.  
  1295.     if (lastNode->Fold || (lastNode->UserData == EMPTY_STACK) || (lastNode->UserData == NULL))
  1296.         last = COMMENT_NONE;
  1297.     else
  1298.         last = GET_FLAGS(lastNode);
  1299.  
  1300.     // continue parsing until no more inconsistencies are found (until context end)
  1301.  
  1302.     refreshStart = (line += lines);
  1303.  
  1304.     for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
  1305.  
  1306.         if (lineNode->Fold)
  1307.  
  1308.             // folds terminate parsing (context end)
  1309.  
  1310.             break;
  1311.  
  1312.         else if (lineNode->UserData == NULL) {
  1313.  
  1314.             // line not yet parsed
  1315.  
  1316.             syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1317.  
  1318.             if (syntaxInfo == EMPTY_STACK)
  1319.  
  1320.                 lineNode->UserData = EMPTY_STACK;
  1321.  
  1322.             else if (syntaxInfo)
  1323.  
  1324.                 lineNode->UserData = DupInfo(syntaxInfo);
  1325.         }
  1326.         else {
  1327.  
  1328.             // check whether highlighting of this line is consistent with previous line
  1329.  
  1330.             new = (lineNode->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(lineNode);
  1331.  
  1332.             if (BadSuccessor(last, new)) {
  1333.  
  1334.                 if (lineNode->UserData != EMPTY_STACK)
  1335.                     FreeVec(lineNode->UserData);
  1336.  
  1337.                 syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1338.  
  1339.                 if (syntaxInfo == EMPTY_STACK)
  1340.  
  1341.                     lineNode->UserData = EMPTY_STACK;
  1342.  
  1343.                 else if (syntaxInfo)
  1344.  
  1345.                     lineNode->UserData = DupInfo(syntaxInfo);
  1346.             }
  1347.             else
  1348.                 break;
  1349.         }
  1350.  
  1351.         if (lineNode->Fold || (lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL))
  1352.             last = COMMENT_NONE;
  1353.         else
  1354.             last = GET_FLAGS(lineNode);
  1355.     }
  1356.  
  1357.     if (refresh) {
  1358.  
  1359.         struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
  1360.  
  1361.         refreshRequest->rr_Line  = refreshStart;
  1362.         refreshRequest->rr_Lines = refresh;
  1363.  
  1364.         return(refreshRequest);
  1365.     }
  1366.     else
  1367.         return(NULL);
  1368. }
  1369.  
  1370.  
  1371. /* ------------------------------- BadSuccessor --------------------------------
  1372.  
  1373.  Return TRUE if syntax information of two adjacent lines is inconsistent.
  1374.  
  1375. */
  1376.  
  1377. BOOL
  1378. BadSuccessor(pred, succ)
  1379.  
  1380. ULONG pred, succ;
  1381. {
  1382.     if (succ & (COMMENT_BODY | COMMENT_END)) {
  1383.  
  1384.         // comment body/end without start ?
  1385.  
  1386.         if ((pred & (COMMENT_START | COMMENT_BODY)) == FALSE)
  1387.  
  1388.             return(TRUE);
  1389.     }
  1390.     else if (pred & COMMENT_START) {
  1391.  
  1392.         // comment start without body/end ?
  1393.  
  1394.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1395.  
  1396.             return(TRUE);
  1397.  
  1398.         // comment inside comment ?
  1399.  
  1400.         if (succ & COMMENT_START)
  1401.  
  1402.             return(TRUE);
  1403.     }
  1404.     else if (pred & COMMENT_BODY) {
  1405.  
  1406.         // body line without end ?
  1407.  
  1408.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1409.  
  1410.             return(TRUE);
  1411.     }
  1412.  
  1413.     return(FALSE);
  1414. }             
  1415.  
  1416. ///
  1417.